#include "renderprocess.h"
#include "global.h"
#include "geometryobjects.h"
#include "enums.h"

#define VECPRODUCT(a, b) Vec(a.x*b.x, a.y*b.y, a.z*b.z)
#define VECDIVIDE(a, b) Vec(a.x/b.x, a.y/b.y, a.z/b.z)

QImage RenderProcess::histogramImage1D() { return m_volume->histogramImage1D(); }
QImage RenderProcess::histogramImage2D() { return m_volume->histogramImage2D(); }

void RenderProcess::setTextureSize(int m) { m_volume->setTextureSize(m); }
void RenderProcess::setMemorySize(int m) { m_volume->setMemorySize(m); }
void RenderProcess::setShowBufferOnly(bool s) { m_showBufferOnly = s; }
void RenderProcess::toggleShowBufferOnly() { m_showBufferOnly = !m_showBufferOnly; }

void RenderProcess::setInterpolateValues(bool iv) { m_mixvol = iv; }
void RenderProcess::setInterpolateValues(float iv) { m_interpVol = iv; }

LightingInformation RenderProcess::lightInfo() { return m_lightInfo; }
void RenderProcess::setLightInfo(LightingInformation lightInfo) { m_lightInfo = lightInfo; }

void RenderProcess::addScreenBox()
{ 
  ScreenBoxObject sb;
  sb.set(QPointF(0.5, 0.5), // position
	 1, // lod
	 0.1, 0.1); // size
  m_screenBox->add(sb);
}
bool RenderProcess::screenBoxGrabsMouse() { return m_screenBox->grabsMouse(); }
bool RenderProcess::screenBoxKeyPressEvent(QKeyEvent* e) { return m_screenBox->keyPressEvent(e); }
void RenderProcess::togglePaintUsingScreenBox() { m_paintUsingScreenBox = !m_paintUsingScreenBox; }

void
RenderProcess::toggleBestLodAlways(bool sticky)
{
  if (sticky)
    {
      m_bestLodAlways = true;
      m_bestLodAlwaysSticky = true;
      m_userLod = false;
      return;
    }
  else
    {      
      m_bestLodAlways = !m_bestLodAlways;
      m_bestLodAlwaysSticky = false;
    }
}

void
RenderProcess::bounds(Vec &bmin, Vec &bmax)
{
  m_boundingBox.bounds(bmin, bmax);
}

void
RenderProcess::setBounds(Vec b0, Vec b1)
{
  Vec bmin, bmax;
  bmin = b0;
  bmax = b1;

  if (m_4DboxSize.x > 0.001)
    {
      // we are handling 4d volume so
      // modify the camera position which is saved
      // with respect to the centroid of the bounding box
      int ts = timeStep();
      Vec corner = Vec(m_4DboxSize.z, m_4DboxSize.y, m_4DboxSize.x*ts);
      bmin += corner;
      bmax += corner;

      ClipInformation clipInfo;
      clipInfo = GeometryObjects::clipplanes()->clipInfo();
      int nclip = clipInfo.size();
      if (nclip > 0) 
	{
	  QList<Vec> cpos;
	  cpos = clipInfo.pos;
	  for(int ci=0; ci<nclip; ci++)
	    cpos[ci].z += corner.z;
	  clipInfo.pos = cpos;
	  GeometryObjects::clipplanes()->set(clipInfo);
	}
    }

  bmin = Vec(qBound(0.0, bmin.x, m_origVolDim.x),
	     qBound(0.0, bmin.y, m_origVolDim.y),
	     qBound(0.0, bmin.z, m_origVolDim.z));
  bmax = Vec(qBound(0.0, bmax.x, m_origVolDim.x),
	     qBound(0.0, bmax.y, m_origVolDim.y),
	     qBound(0.0, bmax.z, m_origVolDim.z));

  bmin = VECPRODUCT(bmin, m_userScale);
  bmax = VECPRODUCT(bmax, m_userScale);
  m_boundingBox.setPositions(bmin, bmax);  

  m_boxMinUser = bmin;
  m_boxMaxUser = bmax;
  m_volume->setUserLimits(m_boxMinUser, m_boxMaxUser);

  GeometryObjects::clipplanes()->setBounds(m_boxMinUser, m_boxMaxUser);

  Vec smin, smax;
  smin = m_boxMinUser;
  smax = m_boxMaxUser;
  smin = VECPRODUCT(smin, m_userScale);
  smax = VECPRODUCT(smax, m_userScale);
  m_viewer->setSceneBoundingBox(smin, smax);
  m_updateBlockLevels = true;
}

void RenderProcess::set4DboxSize(Vec v) { m_4DboxSize = v; }
Vec RenderProcess::boxSize4D() { return m_4DboxSize; }

void
RenderProcess::setTimeStep(int nt, bool flag)
{
  if (m_4DboxSize.x < 0.0001)
    {
      QMessageBox::information(0, "Error", "Please set 4dvolsize first.  This will set the size of single time step in the 4D volume data.");
      return;
    }
  
  if (nt < 0)
    {
      QMessageBox::information(0, "Error",
			       QString("Cannot set %1 time step").arg(nt));
      return;
    }

  Vec bmin, bmax;
  m_boundingBox.bounds(bmin, bmax);

  Vec pos = bmin;
  float blen = bmax.z - bmin.z;

  float b = m_4DboxSize.x * nt;
  if (b >= 0 && b <= m_origVolDim.z &&
      b + blen <= m_origVolDim.z)
    {
      bmin.z = b;
      bmax.z = bmin.z + blen;
    }
  else
    {
      QMessageBox::information(0, "",
			       QString("Timestep %1 does not exist").arg(nt));
      return;
    }

  pos = bmin - pos;

  if (flag)
    {
      Vec eyepos = m_viewer->camera()->position();
      eyepos.z += pos.z;
      m_viewer->camera()->setPosition(eyepos);
    }

  m_volume->setUserLimits(bmin, bmax);
  m_boxMinUser = bmin;
  m_boxMaxUser = bmax;

  Vec smin, smax;
  smin = VECPRODUCT(bmin, m_userScale);
  smax = VECPRODUCT(bmax, m_userScale);
  m_boundingBox.setPositions(smin, smax);  
  m_viewer->setSceneBoundingBox(smin, smax);
  
  m_updateBlockLevels = true;
}

int
RenderProcess::timeStep()
{
  if (m_4DboxSize.x < 0.0001)
    return -1;

  Vec bmin, bmax;
  m_boundingBox.bounds(bmin, bmax);
  
  int nt = bmin.z/m_4DboxSize.x;
  return nt;
}

void
RenderProcess::setBoxSize(Vec bsz)
{
  Vec bmin, bmax;
  bmin = Vec(0,0,0);
  bmax = Vec(qBound(0.0, bsz.z-1, m_origVolDim.x),
	     qBound(0.0, bsz.y-1, m_origVolDim.y),
	     qBound(0.0, bsz.x-1, m_origVolDim.z));
  bmin = VECPRODUCT(bmin, m_userScale);
  bmax = VECPRODUCT(bmax, m_userScale);

  m_boundingBox.setPositions(bmin, bmax);  
  updateBounds();
}
void
RenderProcess::setBoxSize(int nt)
{  
  if (m_4DboxSize.x < 0.0001)
    {
      QMessageBox::information(0, "Error", "Please set 4dvolsize first.  This will set the size of single time step in the 4D volume data.");
      return;
    }
  
  if (nt < 1)
    {
      QMessageBox::information(0, "Error", "Cannot set size for 0 time steps");
      return;
    }

  Vec bsz;
  bsz = m_4DboxSize;
  bsz.x *= nt;
  setBoxSize(bsz);
}

void
RenderProcess::changeTimestep(int t)
{
  Vec bmin, bmax;
  m_boundingBox.bounds(bmin, bmax);

  Vec eyepos = m_viewer->camera()->position();
  
  bool stepChanged = false;
  float zshift = 0.0f;
  if (t > 0)
    {
      if (bmax.z + m_4DboxSize.x <= m_origVolDim.z)
	{
	  zshift = m_4DboxSize.x;
	  stepChanged = true;
	}
    }
  else if (t < 0)
    {
      if (bmin.z - m_4DboxSize.x >= 0)
	{
	  zshift = -m_4DboxSize.x;
	  stepChanged = true;
	}
    }

  if (stepChanged)
    {
      bmin.z += zshift;
      bmax.z += zshift;
      eyepos.z += zshift;

      m_viewer->camera()->setPosition(eyepos);

      //-----------------------------------------------------
      // modify clipInfo
      ClipInformation clipInfo;
      clipInfo = GeometryObjects::clipplanes()->clipInfo();
      int nclip = clipInfo.size();
      if (nclip > 0) 
	{
	  // we are handling 4d volume so
	  // modify the clip position
	  QList<Vec> cpos;
	  cpos = clipInfo.pos;
	  for(int ci=0; ci<nclip; ci++)
	    cpos[ci].z += zshift;
	  
	  clipInfo.pos = cpos;
	  GeometryObjects::clipplanes()->set(clipInfo);
	}
      //-----------------------------------------------------

      m_volume->setUserLimits(bmin, bmax);
      m_boxMinUser = bmin;
      m_boxMaxUser = bmax;

      Vec smin, smax;
      smin = VECPRODUCT(bmin, m_userScale);
      smax = VECPRODUCT(bmax, m_userScale);
      m_boundingBox.setPositions(smin, smax);  
      m_viewer->setSceneBoundingBox(smin, smax);

      m_updateBlockLevels = true;
    }
}

void
RenderProcess::gridSize(int &d, int &r, int &c)
{
  d = m_d;
  r = m_r;
  c = m_c;
}

void RenderProcess::setDepthcue(bool dc)
{
  if (m_raycasterType == Enums::RC_VolumeRender ||
      m_raycasterType == Enums::RC_Slices)
    m_showBufferOnly = false;
  else
    m_showBufferOnly = true;

  m_depthcue = dc;
}

void
RenderProcess::toggleLight()
{
  m_lightInfo.applyLighting = !m_lightInfo.applyLighting;
  m_viewer->update();
}
void
RenderProcess::toggleShadows()
{
  m_lightInfo.applyShadows = !m_lightInfo.applyShadows;
  m_viewer->update();
}

void RenderProcess::changeStep(int t)
{
  m_tstep = qMax(0.1, m_tstep+t*0.1);
  processLUT();
}

Vec RenderProcess::scale() { return VECPRODUCT(m_scale, m_userScale); }

void
RenderProcess::setScale(Vec s)
{
  m_showBufferOnly = false;

  m_userScale = Vec(s.z, s.y, s.x);

  m_boundingBox.setUserScale(m_userScale);

  Vec smin, smax;
  smin = m_boxMinUser;
  smax = m_boxMaxUser;
  smin = VECPRODUCT(smin, m_userScale);
  smax = VECPRODUCT(smax, m_userScale);
  m_viewer->setSceneBoundingBox(smin, smax);
  m_viewer->showEntireScene();
};

void RenderProcess::setStep(float s)
{
  m_showBufferOnly = false;
  m_tstep = s;
  processLUT();
}

void RenderProcess::toggleDepthcue() { m_depthcue = !m_depthcue; }

void
RenderProcess::toggleBounds()
{
  m_showBoundingBox = !m_showBoundingBox;
  if (m_showBoundingBox)
    m_boundingBox.activateBounds();
  else
    {
      m_boundingBox.deactivateBounds();
      updateBounds();
    }
}

void RenderProcess::toggleBox() { m_showBox = !m_showBox; }
void RenderProcess::toggleOuterBox() { m_showOuterBox = !m_showOuterBox; }

RenderProcess::RenderProcess(QObject *parent) : QObject(parent)
{
  m_nVolumes = 1;

  m_screenBoxActive = false;
  m_paintUsingScreenBox = false;
  m_bestLodAlways = false;
  m_bestLodAlwaysSticky = false;

  m_showOuterBox = true;
  m_showBox = true;
  m_showBoundingBox = false;
  m_showBufferOnly = false;
  m_allValidBlocks = false;

  m_mixvol = false;
  m_interpVol = 0.0;

  m_screenDump = 0;

  m_posArray = 0;
  m_posBuffer = 0;

  m_auxTex = 0;
  m_levelTex = 0;
  m_dataTex = 0;
  m_lutTex = 0;
  m_volume = 0;
  m_posTex[0] = 0;
  m_posTex[1] = 0;
  m_colTex[0] = 0;
  m_colTex[1] = 0;
  m_rayTex[0] = 0;
  m_rayTex[1] = 0;

  m_veryFirstRun = true;

  m_blockLevels = 0;

  m_userLodChanged = false;
  m_userLod = false;
  m_userLodSticky = false;
  m_lodUser = -1;
  m_lodMin = 4;
  m_lodCurrent = -1;
  m_bsz = 0;
  m_d = m_r = m_c = 0;
  m_bd = m_br = m_bc = 0;

  m_lut = 0;
  m_olut = 0;

  m_maxBlocks = 100;
  m_isoval = -1;
  m_updateBlockLevels = false;
  m_showIntermediate = true;
  m_interrupt = false;
  m_renderState = false;
  m_resize = false;
  m_recreateShader = false;
  m_depthcue = true;

  m_tstep = 0.5;
  
  m_scale = Vec(1,1,1);
  m_userScale = Vec(1,1,1);

  m_4DboxSize = Vec(0,0,0);

  m_raycasterType = Enums::RC_VolumeRender; // full volume render
  m_surfaceType = 1; // solid
  m_surfaceColor = Vec(0.9, 0.8, 1.0);
  
  m_RCShader = 0;
  m_FBShader = 0;
  m_lightShader = 0;
  m_splatShader = 0;
  m_smoothShader = 0;


  QString sep = QDir::separator();
#ifdef Q_OS_WIN32
  m_shaderDir = qApp->applicationDirPath() + sep + "shaders" + sep;
#endif
#ifdef Q_OS_MACX
  // look in drishti.app/renderplugins
  m_shaderDir = qApp->applicationDirPath()+sep+".."+sep+".."+sep+"shaders"+sep;
#endif

  //-------------------------
  m_volume = new Volume();
  m_volume->start();

  connect(this, SIGNAL(loadBlocks(QList<Vec>, int)),
	  m_volume, SLOT(loadBlocks(QList<Vec>, int)));

  connect(this, SIGNAL(loadAllValidBlocks(int)),
	  m_volume, SLOT(loadAllValidBlocks(int)));

  connect(m_volume, SIGNAL(readDone()),
	  this, SLOT(nextLot()));

  connect(this, SIGNAL(startDraw()),
	  this, SLOT(draw()));

  connect(this, SIGNAL(part1Done(bool)),
	  this, SLOT(loopPart1(bool)));

  connect(this, SIGNAL(runPart1()),
	  this, SLOT(part1()));

  connect(&m_boundingBox, SIGNAL(updated()),
	  this, SLOT(updateBounds()));
  //-------------------------


  //-------------------------
  m_screenBox = new ScreenBox();

  m_tmpArray = new float[100];
}

RenderProcess::~RenderProcess()
{
  delete [] m_tmpArray;

  if (m_lut)
    delete [] m_lut;

  if (m_dataTex)
    glDeleteTextures(1, &m_dataTex);
  m_dataTex = 0;

  if (m_lutTex)
    glDeleteTextures(1, &m_lutTex);
  m_lutTex = 0;

  if (m_posTex[0])
    glDeleteTextures(2, m_posTex);

  if (m_colTex[0])
    glDeleteTextures(2, m_colTex);

  if (m_rayTex[0])
    glDeleteTextures(2, m_rayTex);

  if (m_posBuffer)
    glDeleteFramebuffers(1, &m_posBuffer);
}

void
RenderProcess::setRaycaster(int rc)
{
  m_raycasterType = rc;
  m_recreateShader = true;
}

void
RenderProcess::toggleSurfaceType()
{
  m_surfaceType = !m_surfaceType;
  m_viewer->update();
}
void
RenderProcess::setSurfaceType(bool t)
{
  m_surfaceType = t;
  m_viewer->update();
}

Vec RenderProcess::surfaceColor() { return m_surfaceColor; }
void
RenderProcess::setSurfaceColor(Vec col)
{
  m_surfaceColor = col;
  m_viewer->update();
}

void
RenderProcess::createRaycastShader()
{
  m_recreateShader = false;

  QString flnm;
  QString shaderfile;

  //---------------

  if (m_raycasterType == Enums::RC_VolumeRender)
    flnm = m_shaderDir + "findblocks.fs";
  else if (m_raycasterType == Enums::RC_Slices)
    flnm = m_shaderDir + "findslices.fs";
  else if (m_raycasterType == Enums::RC_Isosurface)
    flnm = m_shaderDir + "findblocks.fs";

  createFindBlocksShader(flnm);

  //---------------

  if (m_raycasterType == Enums::RC_VolumeRender)
    shaderfile = QString("vrraycast%1.fs").arg(m_nVolumes);
  else if (m_raycasterType == Enums::RC_Slices)
    shaderfile = QString("slicesraycast%1.fs").arg(m_nVolumes);
  else if (m_raycasterType == Enums::RC_Isosurface)
    shaderfile = QString("isoraycast%1.fs").arg(m_nVolumes);

  flnm = m_shaderDir + shaderfile;
  createRaycastShader(flnm);

  //---------------

  if (m_raycasterType == Enums::RC_VolumeRender)
    flnm = m_shaderDir + "lightvr.fs";
  else if (m_raycasterType == Enums::RC_Slices)
    flnm = m_shaderDir + "lightvr.fs";
  else if (m_raycasterType == Enums::RC_Isosurface)
    flnm = m_shaderDir + "lightvr.fs";

  createLightShader(flnm);

  //---------------

  if (m_raycasterType == Enums::RC_VolumeRender)
    flnm = m_shaderDir + "smoothvr.fs";
  else if (m_raycasterType == Enums::RC_Slices)
    flnm = m_shaderDir + "smoothvr.fs";
  else if (m_raycasterType == Enums::RC_Isosurface)
    flnm = m_shaderDir + "smoothvr.fs";

  createSmoothShader(flnm);

  //---------------
}

void
RenderProcess::setViewer(QGLViewer *v)
{
  m_log->insertPlainText("renderprocess : viewer set\n");

  m_viewer = v;

  QString flnm;
  flnm = m_shaderDir + "findblocks.fs";
  createFindBlocksShader(flnm);

  flnm = m_shaderDir + "smoothvr.fs";
  createSmoothShader(flnm);

  flnm = m_shaderDir + "splatbuffer.fs";
  createSplatShader(flnm);

  flnm = m_shaderDir + "geoshader.fs";
  createGeometryShader(flnm);

  createRaycastShader();
}

void
RenderProcess::setLogger(QTextEdit *log)
{
  m_log = log;
  m_log->insertPlainText("render thread logger set\n");

  m_volume->setLogger(m_log);
}

void
RenderProcess::setWindowSize(int w, int h)
{
  if (m_windowWidth == w &&
      m_windowHeight == h)
    return;

  m_showBufferOnly = false;

  m_w = w;
  m_h = h;

  if (m_veryFirstRun)
    resize();
  else
    {
      stopRender();
      m_resize = true;
    }
}

void
RenderProcess::resize()
{
  m_windowWidth = m_w;
  m_windowHeight = m_h;

  initPBO();

  m_resize = false;
}

void
RenderProcess::toggleIntermediate()
{
  m_showIntermediate = !m_showIntermediate;
}

void
RenderProcess::setUserLodLevel(int slevel, bool sticky)
{
  m_userLodSticky = sticky;

  m_userLodChanged = true;
  m_lodUser = qMin(slevel, m_volLodMin);

  if (slevel < 0 || slevel > 4)
    {
      m_userLod = false;
      m_userLodSticky = false;
      return;
    }

  stopRender();
  m_userLod = true;
  m_bestLodAlways = false;
  m_bestLodAlwaysSticky = false;
}

void
RenderProcess::setLodLevel(int slevel)
{
  if (m_lodCurrent == slevel)
    return;
    
  m_veryFirstRun = true;
  m_updateBlockLevels = true;

  m_lodCurrent = slevel;

  m_volume->setLodLevel(m_lodCurrent);
  m_volume->gridSize(m_d, m_r, m_c);
  m_volDim = Vec(m_c, m_r, m_d);

  m_bsz = m_volume->blockSize();
  m_bd = m_d/m_bsz + (m_d%m_bsz > 0);
  m_br = m_r/m_bsz + (m_r%m_bsz > 0);
  m_bc = m_c/m_bsz + (m_c%m_bsz > 0);
  m_nblocks = m_bd*m_br*m_bc;

  m_scale = Vec((float)m_oc/(float)m_c,
		(float)m_or/(float)m_r,
		(float)m_od/(float)m_d);
}

void
RenderProcess::setUserLimits(int type, float vmin, float vmax)
{
  m_boxMinUser[type] = (m_origVolDim[type]-1)*qBound(0.0f, vmin, 1.0f);
  m_boxMaxUser[type] = (m_origVolDim[type]-1)*qBound(0.0f, vmax, 1.0f);
  m_volume->setUserLimits(m_boxMinUser, m_boxMaxUser);
  m_updateBlockLevels = true;
  m_log->insertPlainText(QString("Seleced subvolume : %1 %2, %3\n                  : %4 %5 %6\n                  : %7 %8 %9\n"). \
			     arg(m_boxMinUser.x).
			     arg(m_boxMinUser.y).
			     arg(m_boxMinUser.z).
			     arg(m_boxMaxUser.x).
			     arg(m_boxMaxUser.y).
			     arg(m_boxMaxUser.z).
			     arg(m_boxMaxUser.x-m_boxMinUser.x).
			     arg(m_boxMaxUser.y-m_boxMinUser.y).
			     arg(m_boxMaxUser.z-m_boxMinUser.z));

  m_boundingBox.setPositions(m_boxMinUser, m_boxMaxUser);

  m_viewer->setBackgroundColor(Qt::black);

  Vec smin, smax;
  smin = m_boxMinUser;
  smax = m_boxMaxUser;
  smin = VECPRODUCT(smin, m_userScale);
  smax = VECPRODUCT(smax, m_userScale);
  m_viewer->setSceneBoundingBox(smin, smax);
  m_viewer->showEntireScene();
}

void
RenderProcess::updateBounds()
{
  m_boundingBox.bounds(m_boxMinUser, m_boxMaxUser);
  GeometryObjects::clipplanes()->setBounds(m_boxMinUser, m_boxMaxUser);
  if (!m_showBoundingBox)
    {
      m_log->insertPlainText(QString("Seleced subvolume : %1 %2, %3\n                  : %4 %5 %6\n                  : %7 %8 %9\n"). \
			     arg(m_boxMinUser.z).
			     arg(m_boxMinUser.y).
			     arg(m_boxMinUser.x).
			     arg(m_boxMaxUser.z).
			     arg(m_boxMaxUser.y).
			     arg(m_boxMaxUser.x).
			     arg(m_boxMaxUser.z-m_boxMinUser.z).
			     arg(m_boxMaxUser.y-m_boxMinUser.y).
			     arg(m_boxMaxUser.x-m_boxMinUser.x));
      m_volume->setUserLimits(m_boxMinUser, m_boxMaxUser);
      Vec smin, smax;
      smin = m_boxMinUser;
      smax = m_boxMaxUser;
      smin = VECPRODUCT(smin, m_userScale);
      smax = VECPRODUCT(smax, m_userScale);
      m_viewer->setSceneBoundingBox(smin, smax);
      m_viewer->showEntireScene();
      m_updateBlockLevels = true;
    }
}

void
RenderProcess::loadVolume(QList<QString> flnm)
{
  if (!m_volume)
    {
      QMessageBox::information(0, "", "Not yet initialized");
      return;
    }

  if (!m_viewer->isVisible())
    m_viewer->setVisible(true);
  m_viewer->makeCurrent();

  m_nVolumes = flnm.count();  
  createRaycastShader();


  QFileInfo f(flnm[0]);
  Global::setPreviousDirectory(f.absolutePath());

  m_volume->init();
  m_volume->setFilename(flnm);
  m_volume->gridSize(0, m_od, m_or, m_oc);

  m_origVolDim = Vec(m_oc, m_or, m_od);
  m_boxMinUser = Vec(0,0,0);
  m_boxMaxUser = m_origVolDim - Vec(1,1,1);
  m_volume->setUserLimits(m_boxMinUser, m_boxMaxUser);

  m_boundingBox.setBounds(m_boxMinUser, m_boxMaxUser);
  m_boundingBox.setPositions(m_boxMinUser, m_boxMaxUser);
  m_boundingBox.setUserScale(m_userScale);

  m_showBoundingBox = true;
  m_boundingBox.activateBounds();

  //---------------------------------------------------
  // allocate memory so that blocklevels at the finest level can be loaded
  m_bsz = m_volume->blockSize();
  int bd = m_od/m_bsz + (m_od%m_bsz > 0);
  int br = m_or/m_bsz + (m_or%m_bsz > 0);
  int bc = m_oc/m_bsz + (m_oc%m_bsz > 0);
  int nblocks = bd*br*bc;
  if(m_blockLevels) delete [] m_blockLevels;
  m_blockLevels = new uchar[nblocks];
  memset(m_blockLevels, 255, nblocks);
  //---------------------------------------------------

  m_volLodMin = m_volume->minLod();
  m_lodMin = m_volLodMin;

  m_log->insertPlainText(QString("volume : %1, %2, %3\n").\
			 arg(m_od).arg(m_or).arg(m_oc));
  m_log->insertPlainText(QString("volume min lod : %1\n").\
			 arg(m_volLodMin+1));

  if (m_bestLodAlways)
    setLodLevel(finestLod());  
  else
    setLodLevel(m_lodMin);

  initPBO();

  emit switchOffAutoBufferSwapping();
  if (!m_lut) m_lut = new uchar[Global::lutSize()*256*256*4];
  if (!m_olut) m_olut = new float[Global::lutSize()*256*256*4];

  m_viewer->setBackgroundColor(Qt::black);
  Vec smin, smax;
  smin = m_boxMinUser;
  smax = m_boxMaxUser;
  smin = VECPRODUCT(smin, m_userScale);
  smax = VECPRODUCT(smax, m_userScale);
  m_viewer->setSceneBoundingBox(smin, smax);
  m_viewer->showEntireScene();

  Global::setVolumeType(Global::SingleVolume);
}

void
RenderProcess::initPBO()
{
  if (m_screenDump) delete [] m_screenDump;
  m_screenDump = new uchar[3*m_windowWidth*m_windowHeight];
  memset(m_screenDump, 128, 3*m_windowWidth*m_windowHeight);

  if (m_posArray) delete [] m_posArray;
  m_posArray = new float[4*m_windowWidth*m_windowHeight];


  //---------------------------------
  //--- ray posision buffers
  if (m_posBuffer)
    glDeleteFramebuffers(1, &m_posBuffer);
  
  glGenFramebuffers(1, &m_posBuffer);

  if (m_posTex[0])
    glDeleteTextures(2, m_posTex);
  glGenTextures(2, m_posTex);

  for(int i=0; i<2; i++)
    {
      glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_posTex[i]);
      glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
		   0,
		   GL_RGBA32F,
		   m_windowWidth, m_windowHeight,
		   0,
		   GL_RGBA,
		   GL_FLOAT,
		   0);
    }
  //---------------------------------


  //---------------------------------
  //--- accumulation color buffers
  if (m_colTex[0])
    glDeleteTextures(2, m_colTex);
  glGenTextures(2, m_colTex);

  for(int i=0; i<2; i++)
    {
      glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_colTex[i]);
      glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
		   0,
		   GL_RGBA32F,
		   m_windowWidth, m_windowHeight,
		   0,
		   GL_RGBA,
		   GL_FLOAT,
		   0);
    }
  //---------------------------------

  //---------------------------------
  //--- ray direction buffers
  if (m_rayTex[0])
    glDeleteTextures(2, m_rayTex);
  glGenTextures(2, m_rayTex);

  for(int i=0; i<2; i++)
    {
      glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_rayTex[i]);
      glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
		   0,
		   GL_RGBA32F,
		   m_windowWidth, m_windowHeight,
		   0,
		   GL_RGBA,
		   GL_FLOAT,
		   0);
    }
  //---------------------------------
}

void
RenderProcess::setMaxBlocks(int b)
{
  m_maxBlocks = b;
  m_volume->setMaxLoadBlocks(b);
}

void
RenderProcess::processLUT()
{
  if (!m_lut || !m_olut)
    return;

  stopRender();

  m_mutex.lock();
  for (int i=0; i<Global::lutSize()*256*256; i++)
    {
      float r,g,b,a;
      
      r = m_olut[4*i+0];
      g = m_olut[4*i+1];
      b = m_olut[4*i+2];
      a = m_olut[4*i+3];	  

      r = 1-pow((float)(1-r), m_tstep);
      g = 1-pow((float)(1-g), m_tstep);
      b = 1-pow((float)(1-b), m_tstep);
      a = 1-pow((float)(1-a), m_tstep);	  
      
      m_lut[4*i+0] = 255*r;
      m_lut[4*i+1] = 255*g;
      m_lut[4*i+2] = 255*b;
      m_lut[4*i+3] = 255*a;
    }


  if (!m_lutTex)
    glGenTextures(1, &m_lutTex);

  glActiveTexture(GL_TEXTURE4);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, m_lutTex);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D,
	       0, // single resolution
	       4,
	       256, Global::lutSize()*256,
	       0, // no border
	       GL_RGBA,
	       GL_UNSIGNED_BYTE,
	       m_lut);
  glDisable(GL_TEXTURE_2D);

  m_lodMin = m_volume->minLod(m_lut);
  m_lodMin = qMax(m_volLodMin, m_lodMin+1); // just take one level lower
  m_updateBlockLevels = true;
  m_mutex.unlock();
}

float* RenderProcess::getLUT() { return m_olut; }

void
RenderProcess::setLUT(float *olut)
{
  memcpy(m_olut, olut, Global::lutSize()*256*256*4);
  processLUT();
}

void
RenderProcess::setLUT(QList<QImage> imageList)
{
  int imgSize = 256*256*4;
  memset(m_lut, 0, Global::lutSize()*imgSize);
  m_showBufferOnly = false;

  int imgCount = qMin(Global::lutSize(), imageList.count());
  for(int img=0; img<imgCount; img++)
    {
      QImage lutimage = imageList[img].mirrored(false, true);
      uchar *lbits = lutimage.bits();
      for (int i=0; i<256*256; i++)
	{
	  float r,g,b,a;
	  
	  b = (float)lbits[4*i+0]/255.0f;
	  g = (float)lbits[4*i+1]/255.0f;
	  r = (float)lbits[4*i+2]/255.0f;
	  a = (float)lbits[4*i+3]/255.0f;	  
	  
	  m_olut[img*imgSize + 4*i+0] = r*a;
	  m_olut[img*imgSize + 4*i+1] = g*a;
	  m_olut[img*imgSize + 4*i+2] = b*a;
	  m_olut[img*imgSize + 4*i+3] = a;
	}
    }

  processLUT();
}


void
RenderProcess::setIsovalue(int v)
{
  m_showBufferOnly = false;
  stopRender();
  m_mutex.lock();
  m_lodMin = m_volume->minLod(m_isoval);
  m_lodMin = qMax(m_volLodMin, m_lodMin+1); // just take one level lower
  m_updateBlockLevels = true;
  m_isoval = v;
  m_mutex.unlock();
  m_log->insertPlainText(QString("Iso value set to %1\n").arg(m_isoval));
}

void
RenderProcess::updateBlockLevels()
{
  QMutexLocker locker(&m_mutex);
  m_updateBlockLevels = false;
  
  if (m_raycasterType == Enums::RC_VolumeRender)
    m_allValidBlocks = m_volume->initIdentifyValidBlocks(m_lut);
  else if (m_raycasterType == Enums::RC_Slices)
    m_allValidBlocks = m_volume->initIdentifyValidBlocks(m_lut);
  else if (m_raycasterType == Enums::RC_Isosurface)
    m_allValidBlocks = m_volume->initIdentifyValidBlocks(m_isoval);

  memcpy(m_blockLevels, m_volume->blockLevel(), m_nblocks);
  
  loadBlockLevels();
}

void
RenderProcess::enableTextureUnits()
{
  glActiveTexture(GL_TEXTURE1);
  glEnable(GL_TEXTURE_3D);  
}

void
RenderProcess::disableTextureUnits()
{
  glActiveTexture(GL_TEXTURE1);
  glDisable(GL_TEXTURE_3D);
}

bool
RenderProcess::loadAuxDataTextures()
{
  if (!m_dataTex)
    glGenTextures(1, &m_dataTex);

  if (!m_auxTex)
    glGenTextures(1, &m_auxTex);

  uchar *volData;
  uchar *auxData;
  if (m_volume->getNextBlockLot(m_cd, m_cr, m_cc,
				volData,
				auxData))
    {
      int format = GL_LUMINANCE;
      if (m_nVolumes == 2) format = GL_LUMINANCE_ALPHA;
      else if (m_nVolumes == 3) format = GL_RGB;
      else if (m_nVolumes == 4) format = GL_RGBA;

      glActiveTexture(GL_TEXTURE1);
      glEnable(GL_TEXTURE_3D);
      glBindTexture(GL_TEXTURE_3D, m_dataTex);
      glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
      glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 
      glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexImage3D(GL_TEXTURE_3D,
		   0, // single resolution
		   m_nVolumes,
		   m_cc, m_cr, m_cd,
		   0, // no border
		   format,
		   GL_UNSIGNED_BYTE,
		   volData);
      glDisable(GL_TEXTURE_3D);

      glActiveTexture(GL_TEXTURE2);
      glEnable(GL_TEXTURE_3D);
      glBindTexture(GL_TEXTURE_3D, m_auxTex);
      glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
      glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 
      glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      glTexImage3D(GL_TEXTURE_3D,
		   0, // single resolution
		   4,
		   m_bc, m_br, m_bd,
		   0, // no border
		   GL_RGBA,
		   GL_UNSIGNED_BYTE,
		   auxData);
      glDisable(GL_TEXTURE_3D);

      delete [] volData;
      delete [] auxData;
    }
  else // empty! strange
    {
      //QMessageBox::information(0, "Empty !!", "Something wrong !");
      return false;
    }
  return true;
}

void
RenderProcess::bindBuffer(int i)
{
  glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_posBuffer);

  glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
			 GL_COLOR_ATTACHMENT0_EXT,
			 GL_TEXTURE_RECTANGLE_ARB,
			 m_posTex[i],
			 0);

  glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
			 GL_COLOR_ATTACHMENT1_EXT,
			 GL_TEXTURE_RECTANGLE_ARB,
			 m_colTex[i],
			 0);
  
  glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
			 GL_COLOR_ATTACHMENT2_EXT,
			 GL_TEXTURE_RECTANGLE_ARB,
			 m_rayTex[i],
			 0);
  
  GLenum buffers[3] = { GL_COLOR_ATTACHMENT0_EXT,
			GL_COLOR_ATTACHMENT1_EXT,
			GL_COLOR_ATTACHMENT2_EXT };
  glDrawBuffersARB(3, buffers);
}

void
RenderProcess::releaseBuffer()
{
  glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);

  m_viewer->makeCurrent();
}

void
RenderProcess::showBuffer(int i, bool doShadow, bool discardEmpty)
{
  Vec eyepos = m_viewer->camera()->position();
  Vec vd = m_viewer->camera()->viewDirection();

  glUseProgramObjectARB(m_lightShader);
  glUniform1iARB(m_lightparm[0], 0);
  glUniform1iARB(m_lightparm[1], 1);
  glUniform1iARB(m_lightparm[2], 2);

  glUniform3fARB(m_lightparm[3], eyepos.x, eyepos.y, eyepos.z);
  glUniform3fARB(m_lightparm[4], m_lightInfo.userLightVector.x,
		                 m_lightInfo.userLightVector.y,
		                 m_lightInfo.userLightVector.z);
  glUniform3fARB(m_lightparm[5], vd.x, vd.y, vd.z);
  glUniform1iARB(m_lightparm[6], m_windowWidth);
  glUniform1iARB(m_lightparm[7], m_windowHeight);

  if (doShadow)
    glUniform1fARB(m_lightparm[8], m_lightInfo.shadowBlur);
  else
    glUniform1fARB(m_lightparm[8], 0);

  float tstep = m_tstep*qMax(m_scale.x*m_userScale.x,
			     qMax(m_scale.y*m_userScale.y,
				  m_scale.z*m_userScale.z));
  glUniform1fARB(m_lightparm[9], m_lightInfo.shadowFovOffset*tstep);
  glUniform1iARB(m_lightparm[10], discardEmpty);
  glUniform1fARB(m_lightparm[11], m_lightInfo.shadowIntensity);

  int wd = m_windowWidth;
  int ht = m_windowHeight;

  glActiveTexture(GL_TEXTURE0);
  glEnable(GL_TEXTURE_RECTANGLE_ARB);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_rayTex[i]); // volume render
  glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  glActiveTexture(GL_TEXTURE1);
  glEnable(GL_TEXTURE_RECTANGLE_ARB);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_colTex[i]);
  glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  int snTex = m_cpb;
  glActiveTexture(GL_TEXTURE2);
  glEnable(GL_TEXTURE_RECTANGLE_ARB);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_rayTex[snTex]);
  glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  drawScreenQuad(0, wd, ht, 0);

  glUseProgramObjectARB(0);

  glActiveTexture(GL_TEXTURE0);
  glDisable(GL_TEXTURE_RECTANGLE_ARB);
  glActiveTexture(GL_TEXTURE1);
  glDisable(GL_TEXTURE_RECTANGLE_ARB);
  glActiveTexture(GL_TEXTURE2);
  glDisable(GL_TEXTURE_RECTANGLE_ARB);
}

void
RenderProcess::splatBuffer(int i)
{
//  glUseProgramObjectARB(m_splatShader);
//  glUniform1iARB(m_splatparm[0], 0);

  glActiveTexture(GL_TEXTURE0);
  glEnable(GL_TEXTURE_RECTANGLE_ARB);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_colTex[i]);
  int wd = m_windowWidth;
  int ht = m_windowHeight;
  glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  m_viewer->startScreenCoordinatesSystem();

  glColor4f(1, 1, 1, 1);
  glBegin(GL_QUADS);

  glTexCoord2f(0, ht);
  glVertex2f(0, 0);

  glTexCoord2f(0, 0);
  glVertex2f(0, ht);

  glTexCoord2f(wd, 0);
  glVertex2f(wd, ht);

  glTexCoord2f(wd, ht);
  glVertex2f(wd, 0);

  glEnd();

  glUseProgramObjectARB(0);

  glDisable(GL_TEXTURE_RECTANGLE_ARB);
  m_viewer->stopScreenCoordinatesSystem();
}

void
RenderProcess::smoothBuffer()
{
  glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_posBuffer);
  glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
			 GL_COLOR_ATTACHMENT0_EXT,
			 GL_TEXTURE_RECTANGLE_ARB,
			 m_rayTex[m_cpb],
			 0);

  GLenum buffers[1] = { GL_COLOR_ATTACHMENT0_EXT };
  glDrawBuffersARB(1, buffers);

  glActiveTexture(GL_TEXTURE0);
  glEnable(GL_TEXTURE_RECTANGLE_ARB);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_rayTex[m_cpa]);

  glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  Vec eyepos = m_viewer->camera()->position();
  Vec vd = m_viewer->camera()->viewDirection();
  glUseProgramObjectARB(m_smoothShader);
  glUniform1iARB(m_smoothparm[0], 0);
  glUniform3fARB(m_smoothparm[1], eyepos.x, eyepos.y, eyepos.z);
  glUniform3fARB(m_smoothparm[2], vd.x, vd.y, vd.z);
  glUniform1iARB(m_smoothparm[3], m_lightInfo.shadowScale);

  float tstep = m_tstep*qMax(m_scale.x*m_userScale.x,
			     qMax(m_scale.y*m_userScale.y,
				  m_scale.z*m_userScale.z));
  glUniform1fARB(m_smoothparm[4], m_lightInfo.shadowFovOffset*tstep);

  int wd = m_windowWidth;
  int ht = m_windowHeight;
  m_viewer->startScreenCoordinatesSystem();

  glColor4f(1, 1, 1, 1);
  glBegin(GL_QUADS);

  glTexCoord2f(0, ht);
  glVertex2f(0, 0);

  glTexCoord2f(0, 0);
  glVertex2f(0, ht);

  glTexCoord2f(wd, 0);
  glVertex2f(wd, ht);

  glTexCoord2f(wd, ht);
  glVertex2f(wd, 0);

  glEnd();

  glUseProgramObjectARB(0);

  glActiveTexture(GL_TEXTURE0);
  glDisable(GL_TEXTURE_RECTANGLE_ARB);

  m_viewer->stopScreenCoordinatesSystem();

  glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);

//  //---------------------------
//  // swap m_cpa and m_cpb
//  int tmp = m_cpa;
//  m_cpa = m_cpb;
//  m_cpb = tmp;      
//  //---------------------------
}

int
RenderProcess::finestLod()
{
  if (m_lodMin == 0)
    return 0;

  Vec eyepos = m_viewer->camera()->position();
  Vec viewdir = m_viewer->camera()->viewDirection();

  Vec bmin = VECPRODUCT(m_boxMinUser, m_userScale);
  Vec bmax = VECPRODUCT(m_boxMaxUser, m_userScale);
  Vec blen = bmax - bmin;
  
  Vec box[8];
  box[0] = Vec(bmin.x, bmin.y, bmin.z);
  box[1] = Vec(bmax.x, bmin.y, bmin.z);
  box[2] = Vec(bmax.x, bmax.y, bmin.z);
  box[3] = Vec(bmin.x, bmax.y, bmin.z);
  box[4] = Vec(bmin.x, bmin.y, bmax.z);
  box[5] = Vec(bmax.x, bmin.y, bmax.z);
  box[6] = Vec(bmax.x, bmax.y, bmax.z);
  box[7] = Vec(bmin.x, bmax.y, bmax.z);

  
  double minx = 1000000000.0;
  double maxx = -1000000000.0;
  double miny = 1000000000.0;
  double maxy = -1000000000.0;
  for(int i=0; i<8; i++)
    {
      Vec pc = m_viewer->camera()->projectedCoordinatesOf(box[i]);
      minx = qMin(minx, pc.x);
      maxx = qMax(maxx, pc.x);
      miny = qMin(miny, pc.y);
      maxy = qMax(maxy, pc.y);
    }

  float len = (maxx-minx)*(maxx-minx) + (maxy-miny)*(maxy-miny);
  for(int lod=m_lodMin; lod>=0; lod--)
    {
      Vec v = blen/lod;
      if (v.x*v.x + v.y*v.y + v.z*v.z >= len)
	return lod;
    }

  return 0;
}

void
RenderProcess::depthcueLimits(float& dnear, float& dfar)
{
  dnear = 10000000.0;
  dfar = 0;

  Vec eyepos = m_viewer->camera()->position();
  Vec viewdir = m_viewer->camera()->viewDirection();
  Vec bmin = VECPRODUCT(m_boxMinUser, m_userScale);
  Vec bmax = VECPRODUCT(m_boxMaxUser, m_userScale);

  Vec box[8];
  box[0] = Vec(bmin.x, bmin.y, bmin.z);
  box[1] = Vec(bmax.x, bmin.y, bmin.z);
  box[2] = Vec(bmax.x, bmax.y, bmin.z);
  box[3] = Vec(bmin.x, bmax.y, bmin.z);
  box[4] = Vec(bmin.x, bmin.y, bmax.z);
  box[5] = Vec(bmax.x, bmin.y, bmax.z);
  box[6] = Vec(bmax.x, bmax.y, bmax.z);
  box[7] = Vec(bmin.x, bmax.y, bmax.z);

  for(int i=0; i<8; i++)
    {
      Vec ev = box[i]-eyepos;
      float evd = qMax(0.1, viewdir*ev);
      if (evd > 0.0)
	{
	  dnear = min(dnear, evd);
	  dfar = max(dfar, evd);
	}
    }
  float dlen = dfar - dnear;
  dnear += dlen/8; // to make the closer regions brighter
}

void
RenderProcess::drawScreenQuad(float wmin, float wmax,
			      float hmin, float hmax)
{
  int wd = m_windowWidth;
  int ht = m_windowHeight;

  int wdmin, wdmax, htmin, htmax;
  wdmin = 0;
  htmin = 0;
  wdmax = m_windowWidth;
  htmax = m_windowHeight;

  if (wmax != m_windowWidth)
    { 
      wdmin = m_windowWidth*(wmin+1.0f)*0.5f;
      wdmax = m_windowWidth*(wmax+1.0f)*0.5f;
      htmin = m_windowHeight*(hmin+1.0f)*0.5f;
      htmax = m_windowHeight*(hmax+1.0f)*0.5f;
    }

  m_viewer->startScreenCoordinatesSystem();

  glBegin(GL_QUADS);

  glTexCoord2f(wmin, hmin);
  glVertex2f(wdmin, htmin);

  glTexCoord2f(wmin, hmax);
  glVertex2f(wdmin, htmax);

  glTexCoord2f(wmax, hmax);
  glVertex2f(wdmax, htmax);

  glTexCoord2f(wmax, hmin);
  glVertex2f(wdmax, htmin);

  glEnd();

  m_viewer->stopScreenCoordinatesSystem();
}

void
RenderProcess::loadBlockLevels()
{
  if (!m_levelTex)
    glGenTextures(1, &m_levelTex);

  glActiveTexture(GL_TEXTURE3);
  glEnable(GL_TEXTURE_3D);
  glBindTexture(GL_TEXTURE_3D, m_levelTex);
  glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
  glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); 
  glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 
  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexImage3D(GL_TEXTURE_3D,
	       0, // single resolution
	       1,
	       m_bc, m_br, m_bd,
	       0, // no border
	       GL_LUMINANCE,
	       GL_UNSIGNED_BYTE,
	       m_blockLevels);
  glDisable(GL_TEXTURE_3D);

//  int nb = 0;
//  for(int b=0; b<m_nblocks; b++)
//    if (m_blockLevels[b] == 0)
//      nb++;      
//  m_log->insertPlainText(QString("allvalidblocks : %1 %2\n").	\
//			 arg(m_allValidBlocks).arg(nb));
}

void
RenderProcess::findBlocks()
{
  Vec ep = m_viewer->camera()->position();
  Vec vd = m_viewer->camera()->viewDirection();
  Vec rv = m_viewer->camera()->rightVector();
  Vec uv = m_viewer->camera()->upVector();
  float znear = m_viewer->camera()->zNear();
  float ulen = tan(m_viewer->camera()->fieldOfView()/2)*znear;
  float rlen = ulen*m_viewer->camera()->aspectRatio();

  glActiveTexture(GL_TEXTURE3);
  glEnable(GL_TEXTURE_3D);  
  glBindTexture(GL_TEXTURE_3D, m_levelTex);

  glUseProgramObjectARB(m_FBShader);
  glUniform1iARB(m_FBparm[0], 3);
  glUniform3fARB(m_FBparm[2], ep.x, ep.y, ep.z);
  glUniform3fARB(m_FBparm[3], vd.x, vd.y, vd.z);
  glUniform3fARB(m_FBparm[4], rv.x, rv.y, rv.z);
  glUniform3fARB(m_FBparm[5], uv.x, uv.y, uv.z);
  glUniform1fARB(m_FBparm[6], znear);
  glUniform1fARB(m_FBparm[7], rlen);
  glUniform1fARB(m_FBparm[8], ulen);
  glUniform1iARB(m_FBparm[9], m_bd);
  glUniform1iARB(m_FBparm[10], m_br);
  glUniform1iARB(m_FBparm[11], m_bc);
  glUniform1iARB(m_FBparm[12], m_bsz);
  glUniform3fARB(m_FBparm[13], m_scale.z*m_userScale.x,
		               m_scale.y*m_userScale.y,
		               m_scale.x*m_userScale.z);
  glUniform3fARB(m_FBparm[14], m_boxMinUser.x*m_userScale.x,
		               m_boxMinUser.y*m_userScale.y,
		               m_boxMinUser.z*m_userScale.z);
  glUniform3fARB(m_FBparm[15], m_boxMaxUser.x*m_userScale.x,
		               m_boxMaxUser.y*m_userScale.y,
		               m_boxMaxUser.z*m_userScale.z);

  //-------------------------------
  // send clipping information
  QList<Vec> cpos = GeometryObjects::clipplanes()->positions();
  int nclip = qMin(10, cpos.count());
  glUniform1iARB(m_FBparm[16], nclip);
  if (nclip > 0)
    {
      for(int c=0; c<nclip; c++)
	{
	  m_tmpArray[3*c+0] = cpos[c].x;
	  m_tmpArray[3*c+1] = cpos[c].y;
	  m_tmpArray[3*c+2] = cpos[c].z;
	}
      glUniform3fvARB(m_FBparm[17], nclip, m_tmpArray);

      QList<Vec> cnormal = GeometryObjects::clipplanes()->normals();
      for(int c=0; c<nclip; c++)
	{
	  m_tmpArray[3*c+0] = cnormal[c].x;
	  m_tmpArray[3*c+1] = cnormal[c].y;
	  m_tmpArray[3*c+2] = cnormal[c].z;
	}
      glUniform3fvARB(m_FBparm[18], nclip, m_tmpArray);
    }
  //-------------------------------

  glUniform1iARB(m_FBparm[19], m_windowWidth);
  glUniform1iARB(m_FBparm[20], m_windowHeight);

  glColor4f(1, 1, 1, 1);

  if (m_screenBoxActive)
    {
      ScreenBoxObject sb;
      sb = m_screenBox->screenboxes()[m_currScreenBox];
      QPointF pos = sb.position();
      float sx, sy;
      sb.size(sx, sy);

      float wmin, wmax, hmin, hmax;

      wmin = qMax(0.0f, (float)(pos.x()-sx));
      wmax = qMin(1.0f, (float)(pos.x()+sx));
      hmin = qMax(0.0f, (float)(pos.y()-sy));
      hmax = qMin(1.0f, (float)(pos.y()+sy));

      wmin = 2*wmin-1;   wmax = 2*wmax-1;
      hmin = 2*hmin-1;   hmax = 2*hmax-1;

      drawScreenQuad(wmin, wmax, hmin, hmax);
    }
  else
    drawScreenQuad(-1, 1, -1, 1);

  glUseProgramObjectARB(0);

  glDisable(GL_TEXTURE_3D);  
}

void
RenderProcess::raycast()
{
  // raycast into posTex[m_cpb]
  bindBuffer(m_cpb);

  Vec ep = m_viewer->camera()->position();
  Vec vd = m_viewer->camera()->viewDirection();
  Vec rv = m_viewer->camera()->rightVector();
  Vec uv = m_viewer->camera()->upVector();
  float znear = m_viewer->camera()->zNear();
  float ulen = tan(m_viewer->camera()->fieldOfView()/2)*znear;
  float rlen = ulen*m_viewer->camera()->aspectRatio();

  glUseProgramObjectARB(m_RCShader);
  glUniform1iARB(m_RCparm[0], 1); // dataTex
  glUniform1iARB(m_RCparm[1], 3); // levelTex
  glUniform1iARB(m_RCparm[2], 2); // auxTex
  glUniform1iARB(m_RCparm[3], 0); // posTex
  glUniform1iARB(m_RCparm[4], 6); // colTex
  glUniform1iARB(m_RCparm[5], 5); // rayTex

  glUniform3fARB(m_RCparm[6], ep.x, ep.y, ep.z);
  glUniform3fARB(m_RCparm[7], vd.x, vd.y, vd.z);
  glUniform3fARB(m_RCparm[8], rv.x, rv.y, rv.z);
  glUniform3fARB(m_RCparm[9], uv.x, uv.y, uv.z);
  glUniform3fARB(m_RCparm[10], m_scale.z*m_userScale.x,
		               m_scale.y*m_userScale.y,
		               m_scale.x*m_userScale.z);

  glUniform1fARB(m_RCparm[11], znear);
  glUniform1fARB(m_RCparm[12], rlen);
  glUniform1fARB(m_RCparm[13], ulen);

  glUniform1iARB(m_RCparm[14], m_bd);
  glUniform1iARB(m_RCparm[15], m_br);
  glUniform1iARB(m_RCparm[16], m_bc);

  glUniform1iARB(m_RCparm[20], m_bsz);

  glUniform1fARB(m_RCparm[21], (float)m_isoval/255.0f);
  glUniform1fARB(m_RCparm[22], m_tstep);

  float dnear, dfar;
  depthcueLimits(dnear, dfar);
  glUniform1fARB(m_RCparm[23], dnear);
  glUniform1fARB(m_RCparm[24], dfar);

  glUniform1iARB(m_RCparm[25], 4); // lutTex

  glUniform1iARB(m_RCparm[27], m_depthcue);

  glUniform1fARB(m_RCparm[30], m_lightInfo.highlights.ambient);
  glUniform1fARB(m_RCparm[31], m_lightInfo.highlights.diffuse);
  glUniform1fARB(m_RCparm[32], m_lightInfo.highlights.specular);
  glUniform1fARB(m_RCparm[33], (int)pow((float)2, (float)(m_lightInfo.highlights.specularCoefficient)));

  glUniform1fARB(m_RCparm[34], Global::lutSize());

  if (m_nVolumes == 2)
    {
      glUniform1iARB(m_RCparm[35], m_mixvol);
      glUniform1fARB(m_RCparm[36], m_interpVol);
    }

  glUniform3fARB(m_RCparm[28], m_boxMinUser.x*m_userScale.x,
		               m_boxMinUser.y*m_userScale.y,
		               m_boxMinUser.z*m_userScale.z);
  glUniform3fARB(m_RCparm[29], m_boxMaxUser.x*m_userScale.x,
		               m_boxMaxUser.y*m_userScale.y,
		               m_boxMaxUser.z*m_userScale.z);


  //-------------------------------
  // send clipping information
  QList<Vec> cpos = GeometryObjects::clipplanes()->positions();
  int nclip = qMin(10, cpos.count());
  glUniform1iARB(m_RCparm[37], nclip);
  if (nclip > 0)
    {
      for(int c=0; c<nclip; c++)
	{
	  m_tmpArray[3*c+0] = cpos[c].x;
	  m_tmpArray[3*c+1] = cpos[c].y;
	  m_tmpArray[3*c+2] = cpos[c].z;
	}
      glUniform3fvARB(m_RCparm[38], nclip, m_tmpArray);

      QList<Vec> cnormal = GeometryObjects::clipplanes()->normals();
      for(int c=0; c<nclip; c++)
	{
	  m_tmpArray[3*c+0] = cnormal[c].x;
	  m_tmpArray[3*c+1] = cnormal[c].y;
	  m_tmpArray[3*c+2] = cnormal[c].z;
	}
      glUniform3fvARB(m_RCparm[39], nclip, m_tmpArray);
    }
  //-------------------------------

  if (m_raycasterType == Enums::RC_Isosurface)
    {
      glUniform1iARB(m_RCparm[40], m_surfaceType);
      glUniform3fARB(m_RCparm[41], m_surfaceColor.x,
		                   m_surfaceColor.y,
		                   m_surfaceColor.z);
    }

  glActiveTexture(GL_TEXTURE0);
  glEnable(GL_TEXTURE_RECTANGLE_ARB);  
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_posTex[m_cpa]);

  glActiveTexture(GL_TEXTURE3);
  glEnable(GL_TEXTURE_3D);  
  glBindTexture(GL_TEXTURE_3D, m_levelTex);

  glActiveTexture(GL_TEXTURE6);
  glEnable(GL_TEXTURE_RECTANGLE_ARB);  
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_colTex[m_cpa]);

  glActiveTexture(GL_TEXTURE1);
  glEnable(GL_TEXTURE_3D);
  glBindTexture(GL_TEXTURE_3D, m_dataTex);

  glActiveTexture(GL_TEXTURE2);
  glEnable(GL_TEXTURE_3D);  
  glBindTexture(GL_TEXTURE_3D, m_auxTex);

  glActiveTexture(GL_TEXTURE4);
  glEnable(GL_TEXTURE_2D);  
  glBindTexture(GL_TEXTURE_2D, m_lutTex);

  glActiveTexture(GL_TEXTURE5);
  glEnable(GL_TEXTURE_RECTANGLE_ARB);  
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_rayTex[m_cpa]);

//  glActiveTexture(GL_TEXTURE7);
//  glEnable(GL_TEXTURE_RECTANGLE_ARB);  
//  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_rayTex[m_cpa]);

  glColor4f(1, 1, 1, 1);

  if (m_screenBoxActive)
    {
      ScreenBoxObject sb;
      sb = m_screenBox->screenboxes()[m_currScreenBox];
      QPointF pos = sb.position();      
      float sx, sy;
      sb.size(sx, sy);

      float wmin, wmax, hmin, hmax;

      wmin = qMax(0.0f, (float)(pos.x()-sx));
      wmax = qMin(1.0f, (float)(pos.x()+sx));
      hmin = qMax(0.0f, (float)(pos.y()-sy));
      hmax = qMin(1.0f, (float)(pos.y()+sy));

      wmin = 2*wmin-1;   wmax = 2*wmax-1;
      hmin = 2*hmin-1;   hmax = 2*hmax-1;

      drawScreenQuad(wmin, wmax, hmin, hmax);
    }
  else
    drawScreenQuad(-1, 1, -1, 1);

  glUseProgramObjectARB(0);

  glActiveTexture(GL_TEXTURE0);
  glDisable(GL_TEXTURE_RECTANGLE_ARB);  

  glActiveTexture(GL_TEXTURE1);
  glDisable(GL_TEXTURE_3D);  

  glActiveTexture(GL_TEXTURE2);
  glDisable(GL_TEXTURE_3D);  

  glActiveTexture(GL_TEXTURE3);
  glDisable(GL_TEXTURE_3D);  

  glActiveTexture(GL_TEXTURE4);
  glDisable(GL_TEXTURE_2D);  

  glActiveTexture(GL_TEXTURE5);
  glDisable(GL_TEXTURE_RECTANGLE_ARB);  

  glActiveTexture(GL_TEXTURE6);
  glDisable(GL_TEXTURE_RECTANGLE_ARB);  

  glActiveTexture(GL_TEXTURE7);
  glDisable(GL_TEXTURE_RECTANGLE_ARB);  

  releaseBuffer();

  //---------------------------
  // swap m_cpa and m_cpb
  int tmp = m_cpa;
  m_cpa = m_cpb;
  m_cpb = tmp;      
  //---------------------------
}


void
RenderProcess::showLodLevel()
{
  m_viewer->startScreenCoordinatesSystem();
  glDisable(GL_DEPTH_TEST);
  glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);      
  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // blend on top
  glColor4f(0, 0, 0, 0.8f);
  glBegin(GL_QUADS);
  glVertex2f(0, 0);
  glVertex2f(0, 40);
  glVertex2f(100, 40);
  glVertex2f(100, 0);
  glEnd();
  m_viewer->stopScreenCoordinatesSystem();

  glEnable(GL_DEPTH_TEST);
  QFont tfont = QFont("Helvetica", 12);
  tfont.setStyleStrategy(QFont::PreferAntialias);  
  glColor3f(0.8f,0.8f,0.8f);
  glActiveTexture(GL_TEXTURE0);
  glEnable(GL_TEXTURE_2D);
  QString mesg = QString("(%1)").arg(m_lodCurrent+1);
  mesg += QString(".").repeated(m_lodCurrent-finestLod());
  m_viewer->drawText(5, 15, mesg, tfont);

  mesg = "";
  if (m_paintUsingScreenBox) mesg += "p";

  if (m_userLod) mesg += " u";
  if (m_userLodSticky) mesg += "S";

  if (m_bestLodAlways) mesg += " bl";
  if (m_bestLodAlwaysSticky) mesg += "S";

  if (m_showIntermediate) mesg += " s";
  if (m_showBufferOnly) mesg += " f";

  mesg += QString(" %1").arg(m_tstep);
  tfont.setPointSize(10);
  m_viewer->drawText(5, 35, mesg, tfont);

  glDisable(GL_TEXTURE_2D);

  glDisable(GL_BLEND);
}


void
RenderProcess::runByParts()
{
  m_prevKeys.clear();

  if (!m_fastRender &&
      !m_showBoundingBox &&
      !m_userLod &&
      !m_bestLodAlways &&
      m_showIntermediate &&
      m_lodCurrent == m_lodMin)
    memset(m_screenDump, 128, 3*m_windowWidth*m_windowHeight);

  if (m_updateBlockLevels)
    updateBlockLevels();
  
  if (!m_screenBoxActive) // do not clear buffers if drawing within screen box
    {
      bindBuffer(0);
      glClear(GL_COLOR_BUFFER_BIT);
      releaseBuffer();

      bindBuffer(1);
      glClear(GL_COLOR_BUFFER_BIT);
      releaseBuffer();
    }

  if (m_d == 0 ||
      (m_raycasterType == Enums::RC_Isosurface && (m_isoval < 0 || m_isoval > 255)) ||
      (m_raycasterType == Enums::RC_VolumeRender && !m_lut) ||
      (m_raycasterType == Enums::RC_Slices && !m_lut))
   return;

  // reset any remaining blocks to read
  m_volume->clearBlocks();

  //-------------------------------------------------------------
  //start multipass rendering process
  m_cpa = 0;
  m_cpb = 1;

  // update posArray
  bindBuffer(m_cpa);
  findBlocks();
  releaseBuffer();

  if (m_veryFirstRun)
    {
      // Retrieve updated posArray from device
      glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_posBuffer);
      glFramebufferTexture2D(GL_READ_FRAMEBUFFER,
			     GL_COLOR_ATTACHMENT0_EXT,
			     GL_TEXTURE_RECTANGLE_ARB,
			     m_posTex[m_cpa],
			     0);
      glReadPixels(0, 0, m_windowWidth, m_windowHeight,
		   GL_RGBA, GL_FLOAT,
		   m_posArray);
      releaseBuffer();
    }


  m_nextBlocks = false;
  emit part1Done(false);
  //emit part1Done(true);

  //-------------------------------------------------------------
}

bool
RenderProcess::checkRedraw()
{
  bool redraw = false;

  if (m_resize)
    {
      redraw = true;
      resize();
    }

  if (m_recreateShader)
    {
      redraw = true;
      createRaycastShader();
    }
  
  if (m_interrupt && !m_screenBoxActive)
    {
      m_mutex.lock();

      if (m_fastRender)
	{
	  if (m_bestLodAlwaysSticky)
	    setLodLevel(finestLod());  
	  else if (!m_userLod || !m_userLodSticky)
	    setLodLevel(m_lodMin);
	}
      else if (m_bestLodAlways)
	setLodLevel(finestLod());  
      else if (!m_userLod)
	setLodLevel(m_lodMin);

      m_mutex.unlock();
      
      redraw = true;
      m_interrupt = false;
    }

  return redraw;
}

bool
RenderProcess::checkLodChange(bool r)
{
  if (m_screenBoxActive)
    return r;

  bool redraw = r;

  // perform change LOD only when m_showBufferOnly is false
  if (!m_showBufferOnly)
    {
      if (m_userLod)
	{
	  if (m_userLodChanged)
	    {
	      m_userLodChanged = false;
	      setLodLevel(m_lodUser);
	      redraw = true;
	    }
	}
      else
	{
//	  if (!m_fastRender &&
//	      !m_showBoundingBox &&
//	      m_lodCurrent > finestLod())
	  if (!m_fastRender &&
	      !m_showBoundingBox)
	    getScreenDump();

	  if (!m_fastRender &&
	      !redraw &&
	      !m_showBoundingBox)
	    {
	      if (m_lodCurrent > finestLod())
		{
		  setLodLevel(m_lodCurrent-1);
		  redraw = true;
		}
	    }
	}
    }

  return redraw;
}

void
RenderProcess::genSAT(int buf)
{
  // Retrieve updated posArray from device
  glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_posBuffer);
  glFramebufferTexture2D(GL_READ_FRAMEBUFFER,
			 GL_COLOR_ATTACHMENT0_EXT,
			 GL_TEXTURE_RECTANGLE_ARB,
			 m_rayTex[buf],
			 0);
  glReadPixels(0, 0, m_windowWidth, m_windowHeight,
	       GL_RGBA, GL_FLOAT,
	       m_posArray);
  releaseBuffer();

  float *tmpArray = new float[4*m_windowWidth*m_windowHeight];
  memcpy(tmpArray, m_posArray, sizeof(float)*4*m_windowWidth*m_windowHeight);

  for(int id=0; id<m_windowHeight*m_windowWidth; id++)
    {
      if (tmpArray[4*id+3] > -2) // got top surface
	{
	  tmpArray[4*id+0] = 1;
	  tmpArray[4*id+1] = 0;
	  tmpArray[4*id+2] = 0;
	}
      else
	{
	  tmpArray[4*id+0] = 0;
	  tmpArray[4*id+1] = 0;
	  tmpArray[4*id+2] = 0;
	  tmpArray[4*id+3] = 0;
	}
    }

  for(int i=0; i<m_windowHeight; i++)
    for(int j=1; j<m_windowWidth; j++)
      {
	int id = i*m_windowWidth+j;
	int idj= i*m_windowWidth+(j-1);
	tmpArray[4*id+0] += tmpArray[4*idj+0];
	tmpArray[4*id+1] += tmpArray[4*idj+1];
	tmpArray[4*id+2] += tmpArray[4*idj+2];
	tmpArray[4*id+3] += tmpArray[4*idj+3];
      }

  for(int j=0; j<m_windowWidth; j++)
    for(int i=1; i<m_windowHeight; i++)
      {
	int id = i*m_windowWidth+j;
	int idi= (i-1)*m_windowWidth+j;
	tmpArray[4*id+0] += tmpArray[4*idi+0];
	tmpArray[4*id+1] += tmpArray[4*idi+1];
	tmpArray[4*id+2] += tmpArray[4*idi+2];
	tmpArray[4*id+3] += tmpArray[4*idi+3];
      }

  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_rayTex[m_cpb]);
  glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
	       0,
	       GL_RGBA32F,
	       m_windowWidth, m_windowHeight,
	       0,
	       GL_RGBA,
	       GL_FLOAT,
	       tmpArray);

//  {
//    for(int i=1; i<m_windowHeight-1; i++)
//      for(int j=1; j<m_windowWidth-1; j++)
//	{
//	  float v = (tmpArray[4*((i-1)*m_windowWidth+(j-1)) + 3] +
//		     tmpArray[4*((i+1)*m_windowWidth+(j+1)) + 3] -
//		     tmpArray[4*((i+1)*m_windowWidth+(j-1)) + 3] -
//		     tmpArray[4*((i-1)*m_windowWidth+(j+1)) + 3]);
//
//	  float n = (tmpArray[4*((i-1)*m_windowWidth+(j-1)) + 0] +
//		     tmpArray[4*((i+1)*m_windowWidth+(j+1)) + 0] -
//		     tmpArray[4*((i+1)*m_windowWidth+(j-1)) + 0] -
//		     tmpArray[4*((i-1)*m_windowWidth+(j+1)) + 0]);
//
//	  if (n > 0)
//	    tmpArray[4*(i*m_windowWidth+j)+1] = v/n;
//	}
//
//    float tlow = 0;
//    for(int i=1; i<m_windowHeight-1; i++)
//      for(int j=1; j<m_windowWidth-1; j++)
//	{
//	  int id = i*m_windowWidth+j;
//	  tlow = qMax(tmpArray[4*id+1], tlow);
//	}
//    m_log->insertPlainText(QString(" %1 ").arg(tlow));
//
//    uchar *ba = new uchar[4*m_windowWidth*m_windowHeight];
//    for(int id=0; id<m_windowHeight*m_windowWidth; id++)
//      {
//	int v = 255*(tmpArray[4*id+1]/tlow);
//	ba[4*id+0] = v;
//	ba[4*id+1] = v;
//	ba[4*id+2] = v;
//	ba[4*id+3] = 255;
//      }
//
//    QImage bimg(ba, m_windowWidth, m_windowHeight, QImage::Format_ARGB32);
//    QLabel *lbl = new QLabel();
//    lbl->setPixmap(QPixmap::fromImage(bimg));
//    lbl->show();
//    delete [] ba;
//  }

  delete [] tmpArray;
}

void
RenderProcess::loopPart1(bool done)
{
  if (done || m_interrupt)
    {
      if (!m_fastRender &&
	  m_lightInfo.shadowScale>0)
	smoothBuffer();

      if (!m_fastRender && m_lightInfo.applyShadows)
	{
	  if (m_lightInfo.shadowScale<=0)
	    genSAT(m_cpa);
	  else
	    genSAT(m_cpb);
	}

      showBuffer(m_cpa, (!m_fastRender && m_lightInfo.applyShadows), false);
      
      drawBox();

      m_screenBox->draw(m_viewer);

      if (m_viewer->textIsEnabled())
	showLodLevel();

      m_viewer->swapBuffers();

      m_veryFirstRun = false;
      m_renderState = false;

      bool redraw = checkRedraw();
      redraw = checkLodChange(redraw);

      bool dosb = false;

      if (m_screenBoxActive)
	{
	  int nsb = m_screenBox->screenboxes().count();
	  if (m_currScreenBox < nsb-1)
	    { // next screen box
	      m_currScreenBox ++;
	      dosb = true;
	    }
	  else
	    { // all screen boxes handled
	      dosb = false;
	      m_screenBoxActive = false;

	      if (m_userLod)
		setLodLevel(m_lodUser);
	      else if (m_bestLodAlways)
		setLodLevel(finestLod());  
	      else
		setLodLevel(m_lodMin);
	    }
	}
      else if (!m_fastRender &&
	       !redraw &&
	       !m_showBoundingBox && 
	       m_screenBox->isValid())
	{ // first screen box
	  getScreenDump();

	  m_currScreenBox = 0;
	  dosb = true;
	}

      if (dosb) // handle screenbox
	{
	  ScreenBoxObject sb;
	  sb = m_screenBox->screenboxes()[m_currScreenBox];
	  int sblod = qMax(0, sb.lod()-1);
	  if (sblod < m_lodCurrent)
	    {
	      m_lowresBufferId = m_cpa;
	      m_screenBoxActive = true;
	      setLodLevel(sblod);
	      redraw = true;
	    }
	}
      
      if (redraw)
	{
	  m_doRender = true;
	  emit startDraw();
	}
      else
	emit renderDone();
    }
  else
    {
      qApp->processEvents();
      emit runPart1();
    }
}

void
RenderProcess::part1()
{
  if(m_veryFirstRun || m_nextBlocks)
    {
      Vec uscale = Vec(m_scale.z*m_userScale.x,
		       m_scale.y*m_userScale.y,
		       m_scale.x*m_userScale.z);

      int bufsz = m_windowWidth*m_windowHeight;
      QList<Vec> blockList;
      QMap<qint64, Vec> tbl;
      qint64 MAXNB = qint64(9000);
      for(int i=0; i<bufsz; i++)
	{
	  if (m_posArray[4*i+3] > 0)
	    {
	      Vec pos = Vec(m_posArray[4*i+0],
			    m_posArray[4*i+1],
			    m_posArray[4*i+2]);
	      Vec spos = VECDIVIDE(pos, uscale);
	      int kb = floor(spos.x)/m_bsz;
	      int jb = floor(spos.y)/m_bsz;
	      int ib = floor(spos.z)/m_bsz;

//	      int kb = ceil(m_posArray[4*i+0]);
//	      int jb = ceil(m_posArray[4*i+1]);
//	      int ib = ceil(m_posArray[4*i+2]);

	      qint64 bno;
	      bno = kb*MAXNB*MAXNB;
	      bno += jb*MAXNB;
	      bno += ib;
	      tbl.insert(bno, Vec(ib,jb,kb));
	    }
//	  if (m_posArray[4*i+3] < -15)
//	    {
//	      m_log->insertPlainText(QString("(%1 %2 %3) ").\
//				     arg(m_posArray[4*i+0]).\
//				     arg(m_posArray[4*i+1]).\
//				     arg(m_posArray[4*i+2]));
//	    }
	}

      QList<Vec> vals = tbl.values();
      for(int i=0; i<vals.count(); i++)
	  blockList << vals[i];

//      //----------------------------------------------
//      QList<qint64> keys = tbl.keys();
//      if (m_prevKeys.count() == keys.count())
//	{
//	  bool getout = true;
//	  for(int i=0; i<keys.count(); i++)
//	    {
//	      if (keys[i] != m_prevKeys[i])
//		getout = false;
//	    }
//	  if (getout)
//	    blockList.clear();
//	}
//      m_prevKeys = keys;
//      //----------------------------------------------

//      if (blockList.count() == 1)
//	{
//	  //m_log->insertPlainText(QString(" (%1) ").arg(blockList.count()));
//	  //m_log->insertPlainText("\n");
//	  for(int i=0; i<vals.count(); i++)
//	    m_log->insertPlainText(QString("(%1 %2 %3) ").		\
//				   arg(vals[i].x).arg(vals[i].y).arg(vals[i].z));
//	  m_log->insertPlainText("\n");
//
//	  for(int i=0; i<bufsz; i++)
//	    {
//	      if (m_posArray[4*i+3] > 0)
//		{
//		  Vec pos = Vec(m_posArray[4*i+0],
//				m_posArray[4*i+1],
//				m_posArray[4*i+2]);
//		  m_log->insertPlainText(QString("(%1 : %2 %3 %4) ").\
//					 arg(i).arg(pos.x).arg(pos.y).arg(pos.z));
//		}
//	    }
//	  m_log->insertPlainText("\n");
//	}

      if (blockList.count() == 0)
	{
	  emit part1Done(true);
	  return;
	}

      if (m_allValidBlocks)
	emit loadAllValidBlocks(m_lodCurrent);
      else
	emit loadBlocks(blockList, m_lodCurrent);
    }
  else
    nextLot();
}

void
RenderProcess::nextLot()
{
  bool auxDataLoaded = true;
  if(m_veryFirstRun || m_nextBlocks)
    auxDataLoaded = loadAuxDataTextures();

  if (!auxDataLoaded)
    return;

  m_nextBlocks = true;

  raycast();

  //---------------------------
  // Retrieve updated posArray from device
  int bufsz = m_windowWidth*m_windowHeight;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_posBuffer);
  glFramebufferTexture2D(GL_READ_FRAMEBUFFER,
			 GL_COLOR_ATTACHMENT0_EXT,
			 GL_TEXTURE_RECTANGLE_ARB,
			 m_posTex[m_cpa],
			 0);
  glReadPixels(0, 0, m_windowWidth, m_windowHeight,
	       GL_RGBA, GL_FLOAT,
	       m_posArray);
  releaseBuffer();
  //---------------------------

  //---------------------------
  // check if render complete
  bool done = true;
  for(int i=0; i<bufsz; i++)
    {
      if (m_posArray[4*i+3] > 0)
	{
	  done = false;
	  break;
	}
    }
  //---------------------------
  
  //--------------------------------------------------------
  if (m_showIntermediate && !m_fastRender && !m_allValidBlocks)
    {
      if (!m_screenBoxActive || m_lowresBufferId == m_cpa)
	{
	  if (m_screenBoxActive ||
	      (!m_userLod &&
	       !m_bestLodAlways &&
	       m_lodCurrent < m_lodMin))
	    { // blend the current result with previous image
	      glDrawPixels(m_windowWidth,
			   m_windowHeight,
			   GL_RGB,
			   GL_UNSIGNED_BYTE,
			   m_screenDump);
	      glEnable(GL_BLEND);
	      glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // blend on top
	      showBuffer(m_cpa, false, true);
	      glDisable(GL_BLEND);
	    }
	  else // just show the current result
	    showBuffer(m_cpa, false, true);

	  m_viewer->swapBuffers();
	}
    }
  //--------------------------------------------------------

  emit part1Done(done);
}

void
RenderProcess::draw()
{
  if (!m_doRender)
    return;

  if (m_d == 0 ||
      (m_raycasterType == Enums::RC_Isosurface && (m_isoval < 0 || m_isoval > 255)) ||
      (m_raycasterType == Enums::RC_VolumeRender && !m_lut) ||
      (m_raycasterType == Enums::RC_Slices && !m_lut))
    return;

  m_renderState = true;

  m_doRender = false;

  Quaternion rot = m_viewer->camera()->orientation();
  if (m_prevPos != m_viewer->camera()->position() ||
      m_prevRot[0] != rot[0] ||
      m_prevRot[1] != rot[1] ||
      m_prevRot[2] != rot[2] ||
      m_prevRot[3] != rot[3])
    {
      m_showBufferOnly = false;
      m_paintUsingScreenBox = false;
    }

  m_prevPos = m_viewer->camera()->position();
  m_prevRot = m_viewer->camera()->orientation();

  glDisable(GL_DEPTH_TEST);
  glClearColor(0,0,0,0);
  glClearDepth(0.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  bool doloopPart1 = false;
 
  // don't want to start drawing when screenbox is active
  if (screenBoxGrabsMouse())
    doloopPart1 = true;

  // just update the screenbox region
  if (!m_screenBoxActive &&
      m_screenBox->isValid() &&
      m_paintUsingScreenBox)
    doloopPart1 = true;

  if (m_showBufferOnly)
    doloopPart1 = true;

  if (doloopPart1)
    loopPart1(true);
  else
    runByParts();
}

void
RenderProcess::startRender()
{
  if (m_d == 0)
    return;

  m_fastRender = false;
  m_doRender = true;

  if (m_renderState)
    {
      m_mutex.lock();
      m_interrupt = true;
      m_mutex.unlock();
    }
  else
    {
      if (m_userLod)
	setLodLevel(m_lodUser);
      else if (m_bestLodAlways)
	setLodLevel(finestLod());  
      else
	setLodLevel(m_lodMin);
      emit startDraw();
    }
}

void
RenderProcess::fastRender()
{
  if (m_d == 0)
    return;

  m_fastRender = true;
  m_doRender = true;

  if (m_renderState)
    {
      m_mutex.lock();
      m_interrupt = true;
      m_mutex.unlock();
    }
  else
    {
      if (m_bestLodAlwaysSticky)
	setLodLevel(finestLod());  
      else if (!m_userLod)
	setLodLevel(m_lodMin);

      emit startDraw();
    }
}

void
RenderProcess::stopRender()
{
  m_doRender = false;

  if (m_renderState)
    {
      m_mutex.lock();
      m_interrupt = true;
      m_mutex.unlock();
    }
}

void
RenderProcess::drawEnclosingBox(Vec subvolmin,
				Vec subvolmax)
{
  glEnable(GL_LINE_SMOOTH);  // antialias lines	
  glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

  Vec s = (subvolmax-subvolmin);
  int a = qMin(s.x, qMin(s.y, s.z));
  a = qMax(2, (int)(a*0.01));

  // BACK 
  glBegin(GL_QUADS);
  glNormal3f(0,0,-1);
  glVertex3f(subvolmin.x+a, subvolmin.y+a, subvolmin.z);
  glVertex3f(subvolmax.x-a, subvolmin.y+a, subvolmin.z);
  glVertex3f(subvolmax.x-a, subvolmax.y-a, subvolmin.z);
  glVertex3f(subvolmin.x+a, subvolmax.y-a, subvolmin.z);
  glEnd();
  
  // FRONT 
  glBegin(GL_QUADS);  
  glNormal3f(0,0,1);
  glVertex3f(subvolmin.x+a, subvolmin.y+a, subvolmax.z);
  glVertex3f(subvolmax.x-a, subvolmin.y+a, subvolmax.z);
  glVertex3f(subvolmax.x-a, subvolmax.y-a, subvolmax.z);
  glVertex3f(subvolmin.x+a, subvolmax.y-a, subvolmax.z);
  glEnd();
  
  // TOP
  glBegin(GL_QUADS);  
  glNormal3f(0,1,0);
  glVertex3f(subvolmin.x+a, subvolmax.y, subvolmin.z+a);
  glVertex3f(subvolmax.x-a, subvolmax.y, subvolmin.z+a);
  glVertex3f(subvolmax.x-a, subvolmax.y, subvolmax.z-a);
  glVertex3f(subvolmin.x+a, subvolmax.y, subvolmax.z-a);
  glEnd();
  
  // BOTTOM
  glBegin(GL_QUADS);  
  glNormal3f(0,-1,0);
  glVertex3f(subvolmin.x+a, subvolmin.y, subvolmin.z+a);
  glVertex3f(subvolmax.x-a, subvolmin.y, subvolmin.z+a);
  glVertex3f(subvolmax.x-a, subvolmin.y, subvolmax.z-a);
  glVertex3f(subvolmin.x+a, subvolmin.y, subvolmax.z-a);  
  glEnd();  

  // LEFT
  glBegin(GL_QUADS);  
  glNormal3f(-1,0, 0);
  glVertex3f(subvolmin.x, subvolmin.y+a, subvolmin.z+a);
  glVertex3f(subvolmin.x, subvolmin.y+a, subvolmax.z-a);
  glVertex3f(subvolmin.x, subvolmax.y-a, subvolmax.z-a);
  glVertex3f(subvolmin.x, subvolmax.y-a, subvolmin.z+a);  
  glEnd();  

  // RIGHT
  glBegin(GL_QUADS);  
  glNormal3f(1,0, 0);
  glVertex3f(subvolmax.x, subvolmin.y+a, subvolmin.z+a);
  glVertex3f(subvolmax.x, subvolmin.y+a, subvolmax.z-a);
  glVertex3f(subvolmax.x, subvolmax.y-a, subvolmax.z-a);
  glVertex3f(subvolmax.x, subvolmax.y-a, subvolmin.z+a);  
  glEnd();  
}

void
RenderProcess::drawBox()
{
  bool geometryPresent = (GeometryObjects::clipplanes()->count() > 0);

  if (!m_showBoundingBox &&
      !m_showOuterBox &&
      !m_showBox &&
      !geometryPresent)
    return;

  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE, GL_ONE); // blend at the back - make it additive

  glColor4f(0.7f, 0.6f, 0.5f, 0.7f);

  glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);      

  Vec vb = Vec(m_origVolDim.x*m_userScale.x,
	       m_origVolDim.y*m_userScale.y,
	       m_origVolDim.z*m_userScale.z);

  if (m_showBoundingBox || m_showOuterBox)
    {
      drawEnclosingBox(Vec(0,0,0), vb);
    }

  glColor4f(0.7f, 0.7f, 0.7f, 0.7f);

  if (!m_showBoundingBox && m_showBox)
    {
      Vec smin, smax;
      smin = m_boxMinUser;
      smax = m_boxMaxUser;
      smin = VECPRODUCT(smin, m_userScale);
      smax = VECPRODUCT(smax, m_userScale); 

      bool drawit = true;
      if (m_showOuterBox && m_showBox)
	{
	  float s = smin.squaredNorm();
	  s += (m_origVolDim-Vec(1,1,1)-m_boxMaxUser).squaredNorm();
	  if (s < 1)
	    drawit = false;
	}
      if (drawit)
	drawEnclosingBox(smin, smax);
    }

  if (m_showBoundingBox)
    m_boundingBox.draw();

  glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);      


  // draw clipplanes
  GeometryObjects::clipplanes()->draw(m_viewer, false);

  glDisable(GL_BLEND);
}


void
RenderProcess::createRaycastShader(QString flnm)
{ 
  QString shaderString;
  
  QFile fpath(flnm);
  fpath.open(QFile::ReadOnly);
  QTextStream fd(&fpath);
  shaderString = fd.readAll();

  if (m_RCShader)
    glDeleteObjectARB(m_RCShader);

  m_RCShader = glCreateProgramObjectARB();
  if (! loadShader(m_RCShader, shaderString))
    exit(0);

  m_RCparm[0] = glGetUniformLocationARB(m_RCShader, "dataTex");
  m_RCparm[1] = glGetUniformLocationARB(m_RCShader, "levelTex");
  m_RCparm[2] = glGetUniformLocationARB(m_RCShader, "auxTex");
  m_RCparm[3] = glGetUniformLocationARB(m_RCShader, "posTex");
  m_RCparm[4] = glGetUniformLocationARB(m_RCShader, "colTex");
  m_RCparm[5] = glGetUniformLocationARB(m_RCShader, "rayTex");

  m_RCparm[6] = glGetUniformLocationARB(m_RCShader, "eyepos");
  m_RCparm[7] = glGetUniformLocationARB(m_RCShader, "viewdir");
  m_RCparm[8] = glGetUniformLocationARB(m_RCShader, "rightvec");
  m_RCparm[9] = glGetUniformLocationARB(m_RCShader, "upvec");
  m_RCparm[10] = glGetUniformLocationARB(m_RCShader, "scale");

  m_RCparm[11] = glGetUniformLocationARB(m_RCShader, "znear");
  m_RCparm[12] = glGetUniformLocationARB(m_RCShader, "rlen");
  m_RCparm[13] = glGetUniformLocationARB(m_RCShader, "ulen");

  m_RCparm[14] = glGetUniformLocationARB(m_RCShader, "bd");
  m_RCparm[15] = glGetUniformLocationARB(m_RCShader, "br");
  m_RCparm[16] = glGetUniformLocationARB(m_RCShader, "bc");

  m_RCparm[20] = glGetUniformLocationARB(m_RCShader, "bsz");

  m_RCparm[21] = glGetUniformLocationARB(m_RCShader, "isoval");
  m_RCparm[22] = glGetUniformLocationARB(m_RCShader, "tstep");

  m_RCparm[23] = glGetUniformLocationARB(m_RCShader, "dnear");
  m_RCparm[24] = glGetUniformLocationARB(m_RCShader, "dfar");

  m_RCparm[25] = glGetUniformLocationARB(m_RCShader, "lutTex");

  m_RCparm[27] = glGetUniformLocationARB(m_RCShader, "depthcue");

  m_RCparm[28] = glGetUniformLocationARB(m_RCShader, "boxMinUser");
  m_RCparm[29] = glGetUniformLocationARB(m_RCShader, "boxMaxUser");

  m_RCparm[30] = glGetUniformLocationARB(m_RCShader, "ambient");
  m_RCparm[31] = glGetUniformLocationARB(m_RCShader, "diffuse");
  m_RCparm[32] = glGetUniformLocationARB(m_RCShader, "specular");
  m_RCparm[33] = glGetUniformLocationARB(m_RCShader, "speccoeff");

  m_RCparm[34] = glGetUniformLocationARB(m_RCShader, "lutsize");

  m_RCparm[35] = glGetUniformLocationARB(m_RCShader, "interpolateVolumes");
  m_RCparm[36] = glGetUniformLocationARB(m_RCShader, "mixval");

  m_RCparm[37] = glGetUniformLocationARB(m_RCShader, "nclips");
  m_RCparm[38] = glGetUniformLocationARB(m_RCShader, "clipPosition");
  m_RCparm[39] = glGetUniformLocationARB(m_RCShader, "clipNormal");

  m_RCparm[40] = glGetUniformLocationARB(m_RCShader, "surfaceType");
  m_RCparm[41] = glGetUniformLocationARB(m_RCShader, "surfaceColor");
}

void
RenderProcess::createFindBlocksShader(QString flnm)
{ 
  QString shaderString;
  
  QFile fpath(flnm);
  fpath.open(QFile::ReadOnly);
  QTextStream fd(&fpath);
  shaderString = fd.readAll();

  if (m_FBShader)
    glDeleteObjectARB(m_FBShader);

  m_FBShader = glCreateProgramObjectARB();
  if (! loadShader(m_FBShader, shaderString))
    exit(0);

  m_FBparm[0] = glGetUniformLocationARB(m_FBShader, "levelTex");
  m_FBparm[2] = glGetUniformLocationARB(m_FBShader, "eyepos");
  m_FBparm[3] = glGetUniformLocationARB(m_FBShader, "viewdir");
  m_FBparm[4] = glGetUniformLocationARB(m_FBShader, "rightvec");
  m_FBparm[5] = glGetUniformLocationARB(m_FBShader, "upvec");
  m_FBparm[6] = glGetUniformLocationARB(m_FBShader, "znear");
  m_FBparm[7] = glGetUniformLocationARB(m_FBShader, "rlen");
  m_FBparm[8] = glGetUniformLocationARB(m_FBShader, "ulen");
  m_FBparm[9] = glGetUniformLocationARB(m_FBShader, "bd");
  m_FBparm[10] = glGetUniformLocationARB(m_FBShader, "br");
  m_FBparm[11] = glGetUniformLocationARB(m_FBShader, "bc");
  m_FBparm[12] = glGetUniformLocationARB(m_FBShader, "bsz");
  m_FBparm[13] = glGetUniformLocationARB(m_FBShader,"scale");
  m_FBparm[14] = glGetUniformLocationARB(m_FBShader, "boxMinUser");
  m_FBparm[15] = glGetUniformLocationARB(m_FBShader, "boxMaxUser");

  m_FBparm[16] = glGetUniformLocationARB(m_FBShader, "nclips");
  m_FBparm[17] = glGetUniformLocationARB(m_FBShader, "clipPosition");
  m_FBparm[18] = glGetUniformLocationARB(m_FBShader, "clipNormal");

  m_FBparm[19] = glGetUniformLocationARB(m_FBShader, "screenWidth");
  m_FBparm[20] = glGetUniformLocationARB(m_FBShader, "screenHeight");
}

void
RenderProcess::createLightShader(QString flnm)
{ 
  QString shaderString;
  
  QFile fpath(flnm);
  fpath.open(QFile::ReadOnly);
  QTextStream fd(&fpath);
  shaderString = fd.readAll();

  if (m_lightShader)
    glDeleteObjectARB(m_lightShader);

  m_lightShader = glCreateProgramObjectARB();
  if (! loadShader(m_lightShader, shaderString))
    exit(0);

  m_lightparm[0] = glGetUniformLocationARB(m_lightShader, "posTex");
  m_lightparm[1] = glGetUniformLocationARB(m_lightShader, "colTex");
  m_lightparm[2] = glGetUniformLocationARB(m_lightShader, "smoothposTex");
  m_lightparm[3] = glGetUniformLocationARB(m_lightShader, "eyepos");
  m_lightparm[4] = glGetUniformLocationARB(m_lightShader, "lightpos");
  m_lightparm[5] = glGetUniformLocationARB(m_lightShader, "viewdir");
  m_lightparm[6] = glGetUniformLocationARB(m_lightShader, "imageW");
  m_lightparm[7] = glGetUniformLocationARB(m_lightShader, "imageH");
  m_lightparm[8] = glGetUniformLocationARB(m_lightShader, "spread");
  m_lightparm[9] = glGetUniformLocationARB(m_lightShader, "tstep");
  m_lightparm[10] = glGetUniformLocationARB(m_lightShader, "discardEmpty");
  m_lightparm[11] = glGetUniformLocationARB(m_lightShader, "darkening");
}

void
RenderProcess::createSmoothShader(QString flnm)
{ 
  QString shaderString;
  
  QFile fpath(flnm);
  fpath.open(QFile::ReadOnly);
  QTextStream fd(&fpath);
  shaderString = fd.readAll();

  if (m_smoothShader)
    glDeleteObjectARB(m_smoothShader);

  m_smoothShader = glCreateProgramObjectARB();
  if (! loadShader(m_smoothShader, shaderString))
    exit(0);

  m_smoothparm[0] = glGetUniformLocationARB(m_smoothShader, "posTex");
  m_smoothparm[1] = glGetUniformLocationARB(m_smoothShader, "eyepos");
  m_smoothparm[2] = glGetUniformLocationARB(m_smoothShader, "viewdir");
  m_smoothparm[3] = glGetUniformLocationARB(m_smoothShader, "spread");
  m_smoothparm[4] = glGetUniformLocationARB(m_smoothShader, "edge");
}

void
RenderProcess::createSplatShader(QString flnm)
{ 
  QString shaderString;
  
  QFile fpath(flnm);
  fpath.open(QFile::ReadOnly);
  QTextStream fd(&fpath);
  shaderString = fd.readAll();

  if (m_splatShader)
    glDeleteObjectARB(m_splatShader);

  m_splatShader = glCreateProgramObjectARB();
  if (! loadShader(m_splatShader, shaderString))
    exit(0);

  m_splatparm[0] = glGetUniformLocationARB(m_splatShader, "dataTex");
}

void
RenderProcess::createGeometryShader(QString flnm)
{ 
  QString shaderString;
  
  QFile fpath(flnm);
  fpath.open(QFile::ReadOnly);
  QTextStream fd(&fpath);
  shaderString = fd.readAll();

  if (m_geoShader)
    glDeleteObjectARB(m_geoShader);

  m_geoShader = glCreateProgramObjectARB();
  if (! loadShader(m_geoShader, shaderString))
    exit(0);

  m_geoparm[0] = glGetUniformLocationARB(m_geoShader, "posTex");
  m_geoparm[1] = glGetUniformLocationARB(m_geoShader, "colTex");
  m_geoparm[2] = glGetUniformLocationARB(m_geoShader, "eyepos");
  m_geoparm[3] = glGetUniformLocationARB(m_geoShader, "viewdir");
  m_geoparm[4] = glGetUniformLocationARB(m_geoShader, "isosurface");
}

bool
RenderProcess::loadShader(GLhandleARB &progObj,
			  QString shaderString)
{
  GLhandleARB fragObj = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);  
  glAttachObjectARB(progObj, fragObj);

  GLhandleARB vertObj = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);  
  glAttachObjectARB(progObj, vertObj);

  {  // vertObj
    QString qstr;
    qstr = "varying vec3 pointpos;\n";
    qstr += "void main(void)\n";
    qstr += "{\n";
    qstr += "  // Transform vertex position into homogenous clip-space.\n";
    qstr += "  gl_FrontColor = gl_Color;\n";
    qstr += "  gl_BackColor = gl_Color;\n";
    qstr += "  gl_Position = ftransform();\n";
    qstr += "  gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;\n";
    qstr += "  gl_TexCoord[1] = gl_TextureMatrix[1] * gl_MultiTexCoord1;\n";
    qstr += "  gl_TexCoord[2] = gl_TextureMatrix[2] * gl_MultiTexCoord2;\n";
    qstr += "  gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n";
    qstr += "  pointpos = gl_Vertex.xyz;\n";
    qstr += "}\n";
    
    int len = qstr.length();
    char *tbuffer = new char[len+1];
    sprintf(tbuffer, qstr.toAscii().data());
    const char *sstr = tbuffer;
    glShaderSourceARB(vertObj, 1, &sstr, NULL);
    delete [] tbuffer;

    GLint compiled = -1;
    glCompileShaderARB(vertObj);
    glGetObjectParameterivARB(vertObj,
			      GL_OBJECT_COMPILE_STATUS_ARB,
			      &compiled);
    if (!compiled)
      {
	GLcharARB str[1000];
	GLsizei len;
	glGetInfoLogARB(vertObj,
			(GLsizei) 1000,
			&len,
			str);
	
	QMessageBox::information(0,
				 "Error : Vertex Shader",
				 str);
	return false;
    }
  }
    
    
  { // fragObj
    int len = shaderString.length();
    char *tbuffer = new char[len+1];
    sprintf(tbuffer, shaderString.toAscii().data());
    const char *sstr = tbuffer;
    glShaderSourceARB(fragObj, 1, &sstr, NULL);
    delete [] tbuffer;
  
    GLint compiled = -1;
    glCompileShaderARB(fragObj);
    glGetObjectParameterivARB(fragObj,
			    GL_OBJECT_COMPILE_STATUS_ARB,
			      &compiled);
    if (!compiled)
      {
	GLcharARB str[1000];
	GLsizei len;
	glGetInfoLogARB(fragObj,
			(GLsizei) 1000,
			&len,
			str);
	
	//-----------------------------------
	// display error
	
	qApp->beep();
	
	QString estr;
	QStringList slist = shaderString.split("\n");
	for(int i=0; i<slist.count(); i++)
	  estr += QString("%1 : %2\n").arg(i+1).arg(slist[i]);
	
	QTextEdit *tedit = new QTextEdit();
	tedit->insertPlainText("-------------Error----------------\n\n");
	tedit->insertPlainText(str);
	tedit->insertPlainText("\n-----------Shader---------------\n\n");
	tedit->insertPlainText(estr);
	
	QVBoxLayout *layout = new QVBoxLayout();
	layout->addWidget(tedit);
	
	QDialog *showError = new QDialog();
	showError->setWindowTitle("Error in Fragment Shader");
	showError->setSizeGripEnabled(true);
	showError->setModal(true);
	showError->setLayout(layout);
	showError->exec();
	//-----------------------------------
	
	return false;
      }
  }

  
  //----------- link program shader ----------------------
  GLint linked = -1;
  glLinkProgramARB(progObj);
  glGetObjectParameterivARB(progObj, GL_OBJECT_LINK_STATUS_ARB, &linked);
  if (!linked)
    {
      GLcharARB str[1000];
      GLsizei len;
      QMessageBox::information(0,
			       "ProgObj",
			       "error linking texProgObj");
      glGetInfoLogARB(progObj,
		      (GLsizei) 1000,
		      &len,
		      str);
      QMessageBox::information(0,
			       "Error",
			       QString("%1\n%2").arg(len).arg(str));
      return false;
    }

  glDeleteObjectARB(fragObj);
  glDeleteObjectARB(vertObj);

  return true;
}

void
RenderProcess::saveImageToClipboard()
{
  QClipboard *cb = QApplication::clipboard();
  uchar *imgdata = new uchar[4*m_windowWidth*m_windowHeight];
  glReadBuffer(GL_BACK);
  glReadPixels(0,
	       0,
	       m_windowWidth,
	       m_windowHeight,
	       GL_RGBA,
	       GL_UNSIGNED_BYTE,
	       imgdata);
  QImage bimg(imgdata, m_windowWidth, m_windowHeight, QImage::Format_ARGB32);
  bimg = bimg.mirrored();
  bimg = bimg.rgbSwapped();
  cb->setImage(bimg); 
  delete [] imgdata;
}

void
RenderProcess::saveImage()
{
  uchar *imgdata = new uchar[4*m_windowWidth*m_windowHeight];
  glReadBuffer(GL_BACK);
  glReadPixels(0,
	       0,
	       m_windowWidth,
	       m_windowHeight,
	       GL_RGBA,
	       GL_UNSIGNED_BYTE,
	       imgdata);
  QImage bimg(imgdata, m_windowWidth, m_windowHeight, QImage::Format_ARGB32);
  bimg = bimg.mirrored();
  bimg = bimg.rgbSwapped();

  QString flnm;
  flnm = QFileDialog::getSaveFileName(0,
				      "Save snapshot",
				      Global::previousDirectory(),
       "Image Files (*.png *.tif *.bmp *.jpg *.ppm *.xbm *.xpm)");
  
  if (flnm.isEmpty())
    return;

  bimg.save(flnm);

  delete [] imgdata;
}
void
RenderProcess::getScreenDump()
{
  glReadBuffer(GL_BACK);
  glReadPixels(0,
	       0,
	       m_windowWidth,
	       m_windowHeight,
	       GL_RGB,
	       GL_UNSIGNED_BYTE,
	       m_screenDump);
}

QImage
RenderProcess::getImage()
{
  uchar *imgdata = new uchar[4*m_windowWidth*m_windowHeight];
  glReadBuffer(GL_BACK);
  glReadPixels(0,
	       0,
	       m_windowWidth,
	       m_windowHeight,
	       GL_RGBA,
	       GL_UNSIGNED_BYTE,
	       imgdata);
  QImage bimg(imgdata, m_windowWidth, m_windowHeight, QImage::Format_ARGB32);
  bimg = bimg.mirrored();
  bimg = bimg.rgbSwapped();

  QImage image = bimg;
  delete [] imgdata;

  return image;
}

void
RenderProcess::updateLookFrom(Vec pos, Quaternion rot)
{
  Vec cpos = pos;
  if (m_4DboxSize.x > 0.001)
    {
      // we are handling 4d volume so
      // modify the camera position which is saved
      // with respect to the centroid of the bounding box
      Vec bmin, bmax;
      bounds(bmin, bmax);
      Vec cen = (bmax+bmin)/2;
      cpos += cen;
    }

  m_viewer->camera()->setPosition(cpos);
  m_viewer->camera()->setOrientation(rot);

}
void RenderProcess::updateLightVector(Vec dir)
{
  m_lightInfo.userLightVector = dir;
  m_viewer->update();
}
void RenderProcess::applyLighting(bool flag)
{
  m_lightInfo.applyLighting = flag;
  m_viewer->update();
}
void RenderProcess::applyShadows(bool flag)
{
  m_lightInfo.applyShadows = flag;
  m_viewer->update();
}
void RenderProcess::applyEmissive(bool flag) { m_lightInfo.applyEmissive = flag; }
void RenderProcess::updateHighlights(Highlights hl)
{
  m_lightInfo.highlights = hl;
  m_viewer->update();
}
void RenderProcess::updateShadowBlur(float sb)
{
  m_lightInfo.shadowBlur = sb;
  m_viewer->update();
}
void
RenderProcess::updateShadowScale(float ss)
{
  m_lightInfo.shadowScale = ss;
  m_viewer->update();
}
void RenderProcess::updateShadowIntensity(float se)
{
  m_lightInfo.shadowIntensity = se;
  m_viewer->update();
}
void RenderProcess::updateShadowFOV(float sf)
{
  m_lightInfo.shadowFovOffset = sf;
  m_viewer->update();
}

